package com.github.utils;

import com.github.exceptions.HttpErrorStatusException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>Http相关工具</p>
 * @author  Crab2Died
 */
public class HttpUtils {

    private static final    Logger            logger             = Logger.getLogger(HttpUtils.class);

    private static          RequestConfig     requestConfig;
    private static final    int               MAX_TIMEOUT        = 60000;
    private static final    String            CHART_SET          = "UTF-8";
    private static final    String            JSON_CONTENT_TYPE  = "application/json";
    private static final    String            TEXT_CONTENT_TYPE  = "text/json";

    static {
        // 设置连接池
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager();
        // 设置连接池大小
        connMgr.setMaxTotal(10);
        connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());
        connMgr.setValidateAfterInactivity(MAX_TIMEOUT);
        RequestConfig.Builder configBuilder = RequestConfig.custom();
        // 设置连接超时
        configBuilder.setConnectTimeout(MAX_TIMEOUT);
        // 设置读取超时
        configBuilder.setSocketTimeout(MAX_TIMEOUT);
        // 设置从连接池获取连接实例的超时
        configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
        // 在提交请求之前 测试连接是否可用
        // configBuilder.setStaleConnectionCheckEnabled(true);
        requestConfig = configBuilder.build();
    }

    /**
     * <p>form表单参数映射</p>
     * @author  Crab2Died
     *
     * @param params 参数
     */
    private static String paramsFormat(Map<String, Object> params) {

        if (null == params) return "";

        StringBuilder param = new StringBuilder("");
        String connector = "?";
        for (String key : params.keySet()) {
            param.append(connector);
            connector = "&";
            if (params.get(key).getClass().isArray()) {
                Object[] objs = (Object[]) params.get(key);
                for (int j = 0; j < objs.length; j++) {
                    if (j > 0) {
                        param.append(connector);
                    }
                    param.append(key).append("=").append(objs[j] == null ? "" : objs[j]);
                }
            } else {
                param.append(key).append("=").append(params.get(key));
            }
        }
        return param.toString();
    }

    /**
     * <p>关闭response流</p>
     * @@author  Crab2Died
     *
     * @param response 返回流
     */
    private static void closeResponseIO(CloseableHttpResponse response) {

        if (null == response) return;

        try {
            EntityUtils.consume(response.getEntity());
        } catch (IOException e) {
            logger.error("close response IO error!");
            e.printStackTrace();
        }
    }

    @SuppressWarnings("unchecked")
    static public String http(String url, Map<String, Object> params, Class clazz) {

        String result = null;
        CloseableHttpResponse response = null;

        url += paramsFormat(params);

        CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
        try {
            Constructor constructor = clazz.getDeclaredConstructor(new Class[]{String.class});
            constructor.setAccessible(true);
            HttpUriRequest http = (HttpUriRequest) constructor.newInstance(url);
            response = httpclient.execute(http);
            result = resultFormat(response, url);
        } catch (Exception e) {
            logger.error(e.getMessage());
            e.printStackTrace();
        } finally {
            closeResponseIO(response);
        }
        return result;
    }

    static public String get(String url) {
        return http(url, new HashMap<String, Object>(), HttpGet.class);
    }

    static public String get(String url, Map<String, Object> params) {
        return http(url, params, HttpGet.class);
    }

    static public String post(String url) {
        return http(url, new HashMap<String, Object>(), HttpPost.class);
    }

    static public String post(String url, Map<String, Object> params) {
        return http(url, params, HttpPost.class);
    }

    static public String put(String url) {
        return http(url, new HashMap<String, Object>(), HttpPut.class);
    }

    static public String put(String url, Map<String, Object> params) {
        return http(url, params, HttpPut.class);
    }

    static public String delete(String url) {
        return http(url, new HashMap<String, Object>(), HttpDelete.class);
    }

    static public String delete(String url, Map<String, Object> params) {
        return http(url, params, HttpDelete.class);
    }

    @SuppressWarnings("unchecked")
    static public String http(String url, String json, Class clazz) {

        String result = null;
        CloseableHttpResponse response = null;

        CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();

        try {
            Constructor constructor = clazz.getDeclaredConstructor(new Class[]{String.class});
            constructor.setAccessible(true);
            HttpEntityEnclosingRequestBase http = (HttpEntityEnclosingRequestBase) constructor.newInstance(url);

            http.setConfig(requestConfig);
            StringEntity stringEntity = new StringEntity(json, CHART_SET);// 解决中文乱码问题
            stringEntity.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, JSON_CONTENT_TYPE));
            stringEntity.setContentType(TEXT_CONTENT_TYPE);
            http.setEntity(stringEntity);
            response = httpClient.execute(http);
            result = resultFormat(response, url);
        } catch (Exception e) {
            logger.error(e.getMessage());
            e.printStackTrace();
        } finally {
            closeResponseIO(response);
        }
        return result;
    }

    static public String post(String url, String json) {
        return http(url, json, HttpPost.class);
    }

    static public String put(String url, String json) {
        return http(url, json, HttpPut.class);
    }

    private static String resultFormat(CloseableHttpResponse response, String url) throws IOException,
            HttpErrorStatusException {

        if (null == response) return null;
        int code = response.getStatusLine().getStatusCode();
        if (code == HttpStatus.SC_OK) {
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity, CHART_SET);
        } else {
            logger.error(String.format("Http request >> [%s], get the error code : %s", url, code));
            throw new HttpErrorStatusException(String.format("Http request >> [%s], get the error code : %s", url,
                    code));
        }
    }

}
